home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Turnbull China Bikeride
/
Turnbull China Bikeride - Disc 1.iso
/
DEMON
/
RISCOS2
/
TCP_131S.ARC
/
c
/
main
< prev
next >
Wrap
Text File
|
1994-03-07
|
22KB
|
885 lines
/* Main network program - provides both client and server functions */
#define HOSTNAMELEN 32 /* changed from 16 by Bdale 860812 */
#define ESC 29
extern char config[]; /* File to read setup from */
extern char startup[]; /* File to read startup commands from */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <stdarg.h>
#include <ctype.h>
#include "config.h"
#include "global.h"
#include "mbuf.h"
#include "netuser.h"
#include "timer.h"
#include "icmp.h"
#include "iface.h"
#include "ip.h"
#include "tcp.h"
#include "ax25.h"
#include "netrom.h"
#include "remote.h"
#include "ftp.h"
#include "telnet.h"
#include "session.h"
#include "cmdparse.h"
#include "asy.h"
#include "chat.h"
#include "slip.h"
#include "nrs.h"
#include "trace.h"
#include "mem.h"
#include "arp.h"
#include "ax_mbx.h"
#include "finger.h"
#include "internet.h"
#include "kiss.h"
#include "lapb.h"
#include "nr4.h"
#include "ping.h"
#include "pop.h"
#include "nntp.h"
#include "smtp.h"
#include "NetTime.h"
#include "udp.h"
#include "misc.h"
#include "domain.h"
#include "arc.h"
#include "driver.h"
#include "pppdriver.h"
#include "alarm.h"
extern int (*driver_load(char*))(int,...);
/* Dummy structure for loopback tracing */
struct interface loopback = {
NULLIF, "loopback" };
extern struct interface *ifaces;
extern char version[];
extern struct mbuf *loopq;
extern Terminal *MWin;
int mode;
char badhost[] = "Unknown host %s\r\n";
char hostname[HOSTNAMELEN];
unsigned nsessions = NSESSIONS;
int16 lport = 1001;
char *prompt = "net> ";
char nospace[] = "No space!!\r\n"; /* Generic malloc fail message */
int attended = 1;
int ttyflow = 1; /* tty flow control */
static char *ttybuf;
extern int doresolve(int, char **);
static int doexit(int, char **);
static int doattach(int, char **);
static int doecho(int, char **);
static int doeol(int, char **);
static int dohostname(int, char **);
static int dolog(int, char **);
static int dohelp(int, char **);
static int domode(int, char **);
static int doparam(int, char **);
static int doremote(int, char **);
static int dostart(int, char **);
static int dostop(int, char **);
static int dotrace(int, char **);
static int dosource(int, char **);
static int dounattend(int, char **);
static void showtrace(struct interface *);
static int asy_attach(int, char **);
struct cmds cmds[] = {
/* The "go" command must be first */
"", go, 0, NULLCHAR, NULLCHAR,
"arp", doarp, 0, NULLCHAR, NULLCHAR,
"asystat", doasystat, 0, NULLCHAR, NULLCHAR,
"ax25", doax25, 0, NULLCHAR, NULLCHAR,
"attach", doattach, 2, "attach <hardware> <hw specific options>", NULLCHAR,
/* This one is out of alpabetical order to allow abbreviation to "c" */
"connect", doconnect, 3,"connect <interface> <callsign> [digipeaters]",
NULLCHAR,
"chat", dochat, 0, "chat <interface> <error-send> [expect-send pairs]", NULLCHAR,
"chattrace", chat_trace, 0, NULLCHAR, NULLCHAR,
"close", doclose, 0, NULLCHAR, NULLCHAR,
"disconnect", doclose, 0, NULLCHAR, NULLCHAR,
"domain", dodomain, 0, NULLCHAR, NULLCHAR,
"echo", doecho, 0, NULLCHAR, "echo [refuse|accept]",
"eol", doeol, 0, NULLCHAR, "eol options: unix, standard",
"exit", doexit, 0, NULLCHAR, NULLCHAR,
"finger", dofinger, 0, NULLCHAR, NULLCHAR,
"forward", doforward, 0, NULLCHAR, NULLCHAR,
"ftp", doftp, 2, "ftp <address>", NULLCHAR,
"help", dohelp, 0, NULLCHAR, NULLCHAR,
"hop", dohop, 2, NULLCHAR, NULLCHAR,
"hostname", dohostname, 0, NULLCHAR, NULLCHAR,
"kick", dokick, 0, NULLCHAR, NULLCHAR,
"log", dolog, 0, NULLCHAR, NULLCHAR,
"ip", doip, 0, NULLCHAR, NULLCHAR,
"mbox", dombox, 0, NULLCHAR, NULLCHAR,
"mem", domem, 0, NULLCHAR, NULLCHAR,
"mode", domode, 2, "mode <interface>", NULLCHAR,
"netrom", donetrom, 0, NULLCHAR, NULLCHAR,
"nntp", donntp, 0, NULLCHAR, NULLCHAR,
"nrstat", donrstat, 0, NULLCHAR, NULLCHAR,
"param", doparam, 2, "param <interface>", NULLCHAR,
"ping", doping, 0, NULLCHAR, NULLCHAR,
"pop", dopop, 0, NULLCHAR, NULLCHAR,
"quit", doexit, 0, NULLCHAR, NULLCHAR,
"record", dorecord, 0, NULLCHAR, NULLCHAR,
"remote", doremote, 4, "remote <address> <port> <command>", NULLCHAR,
"reset", doreset, 0, NULLCHAR, NULLCHAR,
"resolve", doresolve, 0, NULLCHAR, NULLCHAR,
"route", doroute, 0, NULLCHAR, NULLCHAR,
"session", dosession, 0, NULLCHAR, NULLCHAR,
"smtp", dosmtp, 0, NULLCHAR, NULLCHAR,
"source", dosource, 2, "source <filename>", NULLCHAR,
"start", dostart, 2, "start <servername>",NULLCHAR,
"stop", dostop, 2, "stop <servername>", NULLCHAR,
"tcp", dotcp, 0, NULLCHAR, NULLCHAR,
"telnet", dotelnet, 2, "telnet <address>", NULLCHAR,
"time", dotime, 1, NULLCHAR, NULLCHAR,
"trace", dotrace, 0, NULLCHAR, NULLCHAR,
"udp", doudp, 0, NULLCHAR, NULLCHAR,
"unattended", dounattend, 0, NULLCHAR, NULLCHAR,
"upload", doupload, 0, NULLCHAR, NULLCHAR,
"window", dowin, 0, NULLCHAR, NULLCHAR,
"?", dohelp, 0, NULLCHAR, NULLCHAR,
NULLCHAR, NULLFP, 0,
"Unknown command; type \"?\" for list", NULLCHAR,
};
/* "start" and "stop" subcommands */
static struct cmds startcmds[] = {
"discard", dis1, 0, NULLCHAR, NULLCHAR,
"echo", echo1, 0, NULLCHAR, NULLCHAR,
"finger", finger1, 0, NULLCHAR, NULLCHAR,
"ftp", ftp1, 0, NULLCHAR, NULLCHAR,
"smtp", smtp1, 0, NULLCHAR, NULLCHAR,
"telnet", tn1, 0, NULLCHAR, NULLCHAR,
NULLCHAR, NULLFP, 0,
"start options: discard, echo, finger, ftp, smtp, telnet", NULLCHAR,
};
static struct cmds stopcmds[] = {
"discard", dis0, 0, NULLCHAR, NULLCHAR,
"echo", echo0, 0, NULLCHAR, NULLCHAR,
"finger", finger0, 0, NULLCHAR, NULLCHAR,
"ftp", ftp0, 0, NULLCHAR, NULLCHAR,
"smtp", smtp0, 0, NULLCHAR, NULLCHAR,
"telnet", tn0, 0, NULLCHAR, NULLCHAR,
NULLCHAR, NULLFP, 0,
"stop options: discard, echo, finger, ftp, smtp, telnet", NULLCHAR,
};
static void AutoExec(int at, void *handle)
{
char *args[2];
/* read default start_up file */
args[1] = startup;
dosource(2, args);
}
void net_init(void)
{
char *args[2];
sessions = (struct session *) calloc(nsessions, sizeof(struct session));
memset(udps, NUDP * sizeof(struct udp_cb *), '\0');
ttydriv('\n',&ttybuf); /* Initialise the keyboard buffer */
Read_Domain_File();
/* read default start_up file */
args[1] = config;
dosource(2, args);
cmdmode();
alarm_set(alarm_timenow() + 100, AutoExec, 0);
}
void net_keyboard(int c)
{
int16 cnt;
/* c == 0x1CA means the command escape key (F10) */
if(c == 0x1CA || c == ESC)
{
if(mode != CMD_MODE)
{
cwprintf(NULL, "\r\n");
cmdmode();
}
return;
}
if ((cnt = ttydriv(c,&ttybuf)) == 0)
{
ttyflow = 0; /* stop output to screen */
return;
}
else
{
ttyflow = 1; /* restart output again */
if(mode != CMD_MODE)
go(NULL); /* display pending chars */
}
switch(mode)
{
case CMD_MODE:
cmdparse(cmds, ttybuf, NULL);
break;
case CONV_MODE:
if(current->parse != NULLVFP)
(*current->parse)(NULL, ttybuf, cnt);
break;
}
if(mode == CMD_MODE)
cwprintf(NULL, prompt);
}
void net_poll(void)
{
struct interface *ifp;
struct mbuf *bp;
struct ip ip;
/* Service the loopback queue */
if((bp = dequeue(&loopq)) != NULLBUF)
{
dump(&loopback, IF_TRACE_IN, TRACE_IP, bp); /* Extract IP header */
ntohip(&ip,&bp);
ip_recv(&ip,bp,0);
}
/* Service the interfaces */
for (ifp = ifaces; ifp != NULLIF; ifp = ifp->next)
{
if (ifp->recv != NULLVFP)
{
(*ifp->recv)(ifp);
if (ifp->driver != NULL)
(*ifp->driver)(DRIVER_POLL, ifp->subdevice);
}
}
/* Service the clock if it has ticked */
check_time();
}
/* Standard commands called from main */
/* Enter command mode */
int cmdmode(void)
{
if (mode != CMD_MODE)
{
mode = CMD_MODE;
cooked();
cwprintf(MWin, prompt);
}
return 0;
}
static int doexit(int argc, char **argv)
{
argc = argc;
argv = argv;
net_exit();
return(0);
}
void net_exit(void)
{
iostop();
exit(0);
}
static int dohostname(int argc, char **argv)
{
if(argc < 2)
cwprintf(NULL, "%s\r\n",hostname);
else
strncpy(hostname,argv[1],HOSTNAMELEN);
return 0;
}
static char logname[80];
static int dolog(int argc, char **argv)
{
if(argc < 2)
{
if(logname[0])
cwprintf(NULL, "Logging to %s\r\n", logname);
else
cwprintf(NULL, "Logging off\r\n");
return 0;
}
if(strcmp(argv[1], "stop") != 0)
{
strcpy(logname, argv[1]);
}
else
{
logname[0] = '\0';
}
return 0;
}
static int dohelp(int argc, char **argv)
{
register struct cmds *cmdp;
register char *s;
int i, j;
argc = argc;
argv = argv;
if ((s = malloc(2000)) == NULL) return 0;
sprintf(s, "Main commands:\r\n");
for (i = 0, cmdp = cmds; cmdp->name != NULL; cmdp++, i++)
{
strcat(s, cmdp->name);
if ((i % 4) == 3)
{
strcat(s, "\r\n");
}
else {
for (j = strlen(cmdp->name); j < 16; j++)
strcat(s, " ");
}
}
if ((i % 4) != 0) strcat(s, "\r\n");
cwputs(NULL, s);
free(s);
return 0;
}
static int doecho(int argc, char **argv)
{
extern int refuse_echo;
if(argc < 2)
{
if(refuse_echo)
cwprintf(NULL, "Refuse\r\n");
else
cwprintf(NULL, "Accept\r\n");
}
else
{
if(argv[1][0] == 'r')
refuse_echo = 1;
else if(argv[1][0] == 'a')
refuse_echo = 0;
else
return -1;
}
return 0;
}
static int doremote(int argc, char **argv)
{
struct socket fsock, lsock;
struct mbuf *bp;
argc = argc;
lsock.address = ip_addr;
fsock.address = resolve(argv[1]);
lsock.port = fsock.port = atoi(argv[2]);
bp = alloc_mbuf(1);
if (strcmp(argv[3], "reset") == 0)
{
*bp->data = SYS_RESET;
}
else if (strcmp(argv[3], "exit") == 0)
{
*bp->data = SYS_EXIT;
}
else if (strcmp(argv[3], "kick") == 0)
{
*bp->data = SYS_KICK;
}
else
{
cwprintf(NULL, "Unknown command %s\r\n", argv[3]);
return(1);
}
bp->cnt = 1;
send_udp(&lsock, &fsock, 0, 0, bp, 0, 0, 0);
return(0);
}
/* if unattended mode is set - restrict ax25 and telnet sessions */
static int dounattend(int argc, char **argv)
{
if(argc < 2)
{
if(attended)
cwprintf(NULL, "System in attended operation.\r\n");
else
cwprintf(NULL, "System in unattended operation.\r\n");
}
else
{
if(argv[1][0] == 'y')
{
attended = 0;
cwprintf(NULL, "System now in unattended operation.\r\n");
}
else if(argv[1][0] == 'n')
{
attended = 1;
cwprintf(NULL, "System now in attended operation.\r\n");
}
else
{
cwprintf(NULL, "Usage : unattended y|n\r\n");
return -1;
}
}
return 0;
}
/* set for unix end of line for remote echo mode telnet */
static int doeol(int argc, char **argv)
{
extern int unix_line_mode;
if(argc < 2){
if(unix_line_mode)
cwprintf(NULL, "Unix\r\n");
else
cwprintf(NULL, "Standard\r\n");
}
else {
if(strcmp(argv[1],"unix") == 0)
unix_line_mode = 1;
else if(strcmp(argv[1],"standard") == 0)
unix_line_mode = 0;
else {
return -1;
}
}
return 0;
}
/* Attach an interface
* Syntax: attach <hw type> <hw name> <mode> <label> <bufsize> [<speed>]
*/
static int doattach(int argc, char **argv)
{
extern struct cmds attab[];
return subcmd(attab,argc,argv);
}
/* Manipulate I/O device parameters */
static int doparam(int argc, char **argv)
{
register struct interface *ifp;
for(ifp=ifaces;ifp != NULLIF;ifp = ifp->next)
{
if(strcmp(argv[1],ifp->name) == 0)
break;
}
if(ifp == NULLIF)
{
cwprintf(NULL, "Interface \"%s\" unknown\r\n",argv[1]);
return 1;
}
if(ifp->ioctl == NULLFP)
{
cwprintf(NULL, "Not supported\r\n");
return 1;
}
/* Pass rest of args to device-specific code */
return (*ifp->ioctl)(ifp,argc-2,argv+2);
}
/* Log messages of the form
* Tue Jan 31 00:00:00 1987 44.64.0.7:1003 open FTP
*/
void log_event(struct tcb *tcb, char *fmt, ...)
{
va_list argptr;
char *cp;
time_t t;
FILE *logfp;
if (logname[0] == '\0')
return;
if (logfp = fopen(logname, "a+"), logfp == NULLFILE)
return;
time(&t);
cp = ctime(&t);
rip(cp);
fprintf(logfp, "%s %s - ", cp, psocket(&tcb->conn.remote));
va_start(argptr,fmt);
vfprintf(logfp,fmt,argptr);
va_end(argptr);
fprintf(logfp,"\n");
fclose(logfp);
}
/* Configuration-dependent code */
/* List of supported hardware devices */
struct cmds attab[] = {
/* Ordinary PC asynchronous adaptor */
"asy", asy_attach, 8,
"attach asy <driver> <subdevice> slip|ax25|nrs <label> <buffers> <mtu> <speed>\r\n <nrs call>",
"Could not attach asy",
"ppp", ppp_attach, 1, "attach ppp", "Could not attach ppp",
/* fake netrom interface */
"netrom", nr_attach, 1,
"attach netrom",
"Could not attach netrom",
NULLCHAR, NULLFP, 0,
"Unknown device",
NULLCHAR,
};
/* Protocol tracing function pointers */
void (*tracef[])() = {
ax25_dump,
NULLVFP,
ip_dump,
NULLVFP,
};
/* Attach a serial interface to the system
* argv[0]: hardware type, must be "asy"
* argv[1]: async adapter type, e.g. internal
* argv[2]: async adapter port number
* argv[3]: mode, may be:
* "slip" (point-to-point SLIP)
* "ax25" (AX.25 frame format in SLIP for raw TNC)
* "nrs" (NET/ROM format serial protocol)
* argv[4]: interface label, e.g., "sl0"
* argv[5]: receiver ring buffer size in bytes
* argv[6]: maximum transmission unit, bytes
* argv[7]: interface speed, e.g, "9600"
* argv[8]: optional ax.25 callsign (NRS only)
*/
static int asy_attach(int argc, char **argv)
{
register struct interface *if_asy;
extern struct interface *ifaces;
int dev;
int mode;
struct ax25_addr addr ;
if(nasy >= ASY_MAX){
cwprintf(NULL, "Too many asynch controllers\r\n");
return -1;
}
/* Create interface structure and fill in details */
if_asy = (struct interface *)calloc(1,sizeof(struct interface));
if_asy->name = malloc((unsigned)strlen(argv[4])+1);
strcpy(if_asy->name,argv[4]);
if_asy->mtu = atoi(argv[6]);
if_asy->dev = dev;
if_asy->recv = doslip;
if_asy->stop = asy_stop;
/* Get driver */
if ((if_asy->driver=driver_load(argv[1]))==NULL)
{
cwprintf(NULL, "can't load asy driver \"%s\"\r\n",argv[1]);
return -1;
}
if_asy->subdevice = atoi(argv[2]);
if(strcmp(argv[3],"slip") == 0)
mode = SLIP_MODE;
else if(strcmp(argv[3],"ax25") == 0)
mode = AX25_MODE;
else if(strcmp(argv[3],"nrs") == 0)
mode = NRS_MODE;
else
mode = UNKNOWN;
dev = nasy++;
switch(mode)
{
case SLIP_MODE:
if_asy->ioctl = asy_ioctl;
if_asy->send = (int(*)())slip_send;
if_asy->output = NULLFP; /* ARP isn't used */
if_asy->raw = slip_raw;
if_asy->flags = 0;
slip[dev].recv = slip_recv;
break;
case AX25_MODE: /* Set up a SLIP link to use AX.25 */
axarp();
if(argc < 9)
{
/* no call sign supplied */
if(mycall.call[0] == '\0')
{
cwprintf(NULL, "set mycall first or specify in attach statement\r\n");
free(if_asy->name);
free((char *)if_asy);
nasy--;
return -1;
}
else
{
addr = mycall;
}
}
else
{
/* callsign supplied on attach line */
if(setcall(&addr,argv[8]) == -1)
{
cwprintf (NULL, "bad callsign on attach line\r\n");
free(if_asy->name);
free((char *)if_asy);
nasy--;
return -1;
}
}
if_asy->ioctl = kiss_ioctl;
if_asy->send = (int(*)())ax_send;
if_asy->output = (int(*)())ax_output;
if_asy->raw = kiss_raw;
if(if_asy->hwaddr == NULLCHAR)
if_asy->hwaddr = malloc(sizeof(addr));
memcpy(if_asy->hwaddr,(char *)&addr,sizeof(addr));
slip[dev].recv = kiss_recv;
break;
case NRS_MODE: /* Set up a net/rom serial interface */
if(argc < 9)
{
/* no call supplied? */
if(mycall.call[0] == '\0')
{
/* try to use default */
cwprintf(NULL, "set mycall first or specify in attach statement\r\n");
return -1;
}
else
addr = mycall;
}
else
{
/* callsign supplied on attach line */
if(setcall(&addr,argv[8]) == -1){
cwprintf (NULL, "bad callsign on attach line\r\n");
free(if_asy->name);
free((char *)if_asy);
nasy--;
return -1;
}
}
if_asy->recv = nrs_recv;
if_asy->ioctl = asy_ioctl;
if_asy->send = (int(*)())ax_send;
if_asy->output = (int(*)())ax_output;
if_asy->raw = nrs_raw;
if(if_asy->hwaddr == NULLCHAR)
if_asy->hwaddr = malloc(sizeof(addr));
memcpy(if_asy->hwaddr,(char *)&addr,sizeof(addr));
nrs[dev].iface = if_asy;
break;
default:
cwprintf(NULL, "Mode %s unknown for interface %s\r\n",
argv[2],argv[4]);
free(if_asy->name);
free((char *)if_asy);
nasy--;
return -1;
}
if_asy->next = ifaces;
ifaces = if_asy;
asy_init(dev, if_asy, (unsigned)atoi(argv[5]));
if (atoi(argv[7]))
asy_speed(dev, atoi(argv[7]));
return 0;
}
/* Display or set IP interface control flags */
static int domode(int argc, char **argv)
{
register struct interface *ifp;
for(ifp=ifaces;ifp != NULLIF;ifp = ifp->next){
if(strcmp(argv[1],ifp->name) == 0)
break;
}
if(ifp == NULLIF){
cwprintf(NULL, "Interface \"%s\" unknown\r\n",argv[1]);
return 1;
}
if(argc < 3){
cwprintf(NULL, "%s: %s\r\n",ifp->name,
(ifp->flags & CONNECT_MODE) ? "VC mode" : "Datagram mode");
return 0;
}
switch(argv[2][0]){
case 'v':
case 'c':
case 'V':
case 'C':
ifp->flags = CONNECT_MODE;
break;
case 'd':
case 'D':
ifp->flags = DATAGRAM_MODE;
break;
default:
cwprintf(NULL, "Usage: %s [vc | datagram]\r\n",argv[0]);
return 1;
}
return 0;
}
static int dostart(int argc, char **argv)
{
return subcmd(startcmds,argc,argv);
}
static int dostop(int argc, char **argv)
{
return subcmd(stopcmds,argc,argv);
}
static int dotrace(int argc, char **argv)
{
struct interface *ifp;
struct interface *ifpp;
int tracing = 0;
if(argc < 2){
showtrace(&loopback);
for(ifp = ifaces; ifp != NULLIF; ifp = ifp->next)
showtrace(ifp);
return 0;
}
if(strcmp("loopback",argv[1]) == 0)
ifp = &loopback;
else
for(ifp = ifaces; ifp != NULLIF; ifp = ifp->next)
if(strcmp(ifp->name,argv[1]) == 0)
break;
if(ifp == NULLIF){
cwprintf(NULL, "Interface %s unknown\r\n",argv[1]);
return 1;
}
if(argc >= 3){
if ((ifp->trace = htoi(argv[2])) > 0)
{
twopen();
}
else {
for(ifpp = ifaces; ifpp != NULLIF; ifpp = ifpp->next)
if (ifpp->trace > 0)
tracing = 1;
if (loopback.trace > 0)
tracing = 1;
if (!tracing)
twclose();
}
}
showtrace(ifp);
return 0;
}
/* Display the trace flags for a particular interface */
static void showtrace(register struct interface *ifp)
{
if(ifp == NULLIF)
return;
cwprintf(NULL, "%s:",ifp->name);
if(ifp->trace & (IF_TRACE_IN | IF_TRACE_OUT)){
if(ifp->trace & IF_TRACE_IN)
cwprintf(NULL, " input");
if(ifp->trace & IF_TRACE_OUT)
cwprintf(NULL, " output");
if(ifp->trace & IF_TRACE_HEX)
cwprintf(NULL, " (Hex/ASCII dump)");
else if(ifp->trace & IF_TRACE_ASCII)
cwprintf(NULL, " (ASCII dump)");
else
cwprintf(NULL, " (headers only)");
cwprintf(NULL, "\r\n");
}
else
cwprintf(NULL, " tracing off\r\n");
}
void stoptrace(void)
{
struct interface *ifp;
loopback.trace = 0;
for(ifp = ifaces; ifp != NULLIF; ifp = ifp->next)
ifp->trace = 0;
}
static int dosource(int argc, char **argv)
{
static char inbuf[200], savebuf[200]; /* keep it off the stack */
char filename[80]; /* for error messages */
int linenum = 0;
FILE *fp;
argc = argc;
if ((fp = fopen(argv[1], "r")) == NULLFILE)
{
cwprintf(NULL, "%s: cannot open\r\n", argv[1]);
return 1;
}
strcpy(filename, argv[1]);
while (fgets(inbuf, sizeof(inbuf),fp) != NULLCHAR)
{
rip(inbuf);
strcpy(savebuf, inbuf);
cwprintf(NULL, "%s\r\n", inbuf);
linenum++;
if (cmdparse(cmds, inbuf, NULL) != 0)
cwprintf(NULL, "*** file \"%s\", line %d: %s\r\n", filename, linenum, savebuf);
}
fclose(fp);
return 0;
}